/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.vojna.ant_alg.algorithms;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

import pl.vojna.ant_alg.PbbFunction;
import pl.vojna.ant_alg.Problem;

/**
 * 
 * @author rusak
 */
public class Ant {

	private int size;
	private int[] solution;
	private ArrayList<Integer> neighFacilities, neighLocations;
	private Random r = new Random();
	private Problem problem;
	private double cost;
	private final PbbFunction pbbFunction;

	public Ant(int size, Problem problem, PbbFunction pbbFunction) {
		this.size = size;
		this.problem = problem;
		this.pbbFunction = pbbFunction;
		cost = 0;
		neighFacilities = new ArrayList<Integer>();
		neighLocations = new ArrayList<Integer>();

		for (int i = 0; i < size; i++) {
			neighFacilities.add(i);
			neighLocations.add(i);
		}
		solution = new int[size];
		for (int i = 0; i < size; i++) {
			solution[i] = -1;
		}
	}

	// zwraca dwa inty - fabrykę i lokalizację, do której fabryka została
	// przydzielona w tej turze.
	public int[] nextStep(double pheromoneTrails[][]) {
		int chosenI, chosenJ;

		int stepSize = neighFacilities.size();
		if (stepSize == 1) {
			Integer fac = neighFacilities.get(0);
			Integer loc = neighLocations.get(0);
			neighFacilities.remove(0);
			neighLocations.remove(0);
			solution[loc] = fac;
			adjustCost(loc);
			return new int[] { fac, loc };
		} else if (stepSize > 1) {
			double probabilities[][] = new double[stepSize][stepSize];
			double probTable[] = new double[stepSize * stepSize];

			Integer facilities[] = new Integer[stepSize];
			neighFacilities.toArray(facilities);
			Integer locations[] = new Integer[stepSize];
			neighLocations.toArray(locations);
			double sum = 0;
			for (int i = 0; i < stepSize; i++) {
				for (int j = 0; j < stepSize; j++) {
					probabilities[i][j] = pbbFunction.call(facilities[i],
							locations[j]);

					sum += probabilities[i][j];
				}
			}

			int where;

			for (int i = 0; i < stepSize; i++) {
				for (int j = 0; j < stepSize; j++) {

					probabilities[i][j] /= sum;

					if ((where = i * stepSize + j) != 0) {
						probTable[where] = probTable[where - 1]
								+ probabilities[i][j];

					} else {
						probTable[0] = probabilities[0][0];
					}

				}
			}

			Arrays.sort(probTable);
			double random = r.nextDouble();
			// System.out.println("RANDOM: " + random);
			int k = Arrays.binarySearch(probTable, random);
			int chosenIndex = k > -1 ? k : (-1) * k - 1;
			// System.out.println("CHOSEN INDEX: " + chosenIndex);
			chosenI = chosenIndex / stepSize;
			chosenJ = chosenIndex - chosenI * stepSize;
			neighFacilities.remove(chosenI);
			neighLocations.remove(chosenJ);
			solution[locations[chosenJ]] = facilities[chosenI];

			adjustCost(locations[chosenJ]);

			return new int[] { facilities[chosenI], locations[chosenJ] };
		}

		return null;

	}

	public double getCost() {
		return cost;
	}

	public int[] getSolution() {
		return solution;
	}

	private void adjustCost(int chosenLocation) {

		for (int i = 0; i < size; i++) {
			if (solution[i] != -1) {
				cost += problem.flows[solution[chosenLocation]][solution[i]]
						* problem.distances[chosenLocation][i];
				if (i != chosenLocation) {
					cost += problem.flows[solution[i]][solution[chosenLocation]]
							* problem.distances[i][chosenLocation];
				}

			}
		}
	}
}
